Candace Savonen - CCDL for ALSF
This notebook sets up the MAF data files as a combined file for ready comparison in 02-analyze-concordance.Rmd and also does some first line analyses from ready-made maftools functions.
It is the first notebook in this series which addresses issue # 30 in OpenPBTA.
Summary of the Set Up:
For both the Strelka2 and MuTect2 datasets, the data is imported by maftools::read.maf and the corresponding clinical data from pbta-histologies.tsv is added to the object. This is only done once as written (as the read.maf is very memory intensive) and each MuTect2 and Strelka2 are saved to an RDS file for faster and ready reloading.
For both Strelka2 and MuTect2 data, the following variables are calculated and added into the combined dataset for analysis in 02-analyze-concordance.Rmd.
Variables created:
vaf : Variant Allele Frequency = (t_alt_count) / (t_ref_count + t_alt_count).
mutation_id: Used to determine whether two variants calls are identical in both datasets. It is a concatenation of: Hugo_Symbol, change, Start_Position, and Tumor_Sample_Barcode (the sample ID).
base_change: variable that indicates the exact change in bases (e.g. ‘T>C’).
change: indicates the base_change information but groups together deletions, insertions, and long (more than a SNV) as their own groups.
coding: Summarize the BIOTYPE variable for whether or not it is a coding gene.
The output files from this notebook:
scratch/strelka2.RDS
scratch/mutect2.RDS
scratch/metadata_filtered_maf_samples.tsv
analyses/mutect2-vs-strelka2/plots/sample_cor_mutect2_vs_strelka2.pdf
analyses/mutect2-vs-strelka2/plots/sample_cor_mutect2_vs_strelka2.pdf
analyses/mutect2-vs-strelka2/results/combined_results.tsv
Usage
To run this from the command line, use:
Rscript -e "rmarkdown::render('analyses/mutect2-vs-strelka2/01-set-up.Rmd',
clean = TRUE)"
This assumes you are in the top directory of the repository.
Set Up
# We need maftools - this will be added to the running Docker issue whenever it is up
if (!("maftools" %in% installed.packages())) {
if (!requireNamespace("BiocManager", quietly = TRUE)) {
install.packages("BiocManager")
}
BiocManager::install("maftools")
}
# Will need hexbin for the hex plot
if (!("hexbin" %in% installed.packages())) {
install.packages("hexbin")
}
Get magrittr pipe
`%>%` <- dplyr::`%>%`
Directories and files
Path to the symlinked data obtained via bash download-data.sh.
data_dir <- file.path("..", "..", "data")
scratch_dir <- file.path("..", "..", "scratch")
Create output directories in this analysis folder.
if (!dir.exists("results")) {
dir.create("results")
}
if (!dir.exists("plots")) {
dir.create("plots")
}
Functions
Set up the function that will create the new variables from the maf objects.
Description of variables
The function, set_up_variables, will calculate the following variables from a maf object:
- Calculate VAF for each. The VAFs for each variation in each dataset are calculated by t_alt_count) / (t_ref_count + t_alt_count), as following the code used in maftools
- Create a base_change variable that indicates the exact change in bases.
- Create a change variable for each which indicates the base_change information but groups together deletions, insertions, and long (more than a SNV) as their own groups.
- Make a mutation id by concatenating Hugo_Symbol, change, Start_Position, and Tumor_Sample_Barcode (the sample ID).
We will use this variable to determine whether two variants calls are identical in both datasets. - Summarize the BIOTYPE variable for whether or not it is a coding gene.
set_up_variables <- function(maf = NULL) {
# Creates these new variables from a maf object provided: VAF, mutation_id,
# base_change, change, coding.
#
# Args:
# maf: a maf object to create new variables from
#
# Returns:
# a data.frame with all the original information in the `@data` part of the
# maf object but with these new variables: VAF, mutation_id, base_change,
# change, coding.
# Extract the data part of the maf object, put it through a dplyr pipe.
df <- maf@data
df %>%
dplyr::mutate(
# Calculate the variant allele frequency
vaf = as.numeric(t_alt_count) / (as.numeric(t_ref_count) +
as.numeric(t_alt_count)),
# Create a base_change variable
base_change = paste0(Reference_Allele, ">", Allele),
# Create a variable that notes whether the variant is in a
# coding/non-coding region
coding = dplyr::case_when(
BIOTYPE != "protein_coding" ~ "non-coding",
TRUE ~ "protein_coding"
)
) %>%
dplyr::mutate(
# From the base_change variable, summarize insertions, deletions, and
# changes that are more than one base into their own groups.
change = dplyr::case_when(
grepl("^-", base_change) ~ "insertion",
grepl("-$", base_change) ~ "deletion",
nchar(base_change) > 3 ~ "long_change",
TRUE ~ base_change
)) %>%
dplyr::mutate(
# Create the mutation id based on the change variable as well as the
# gene symbol, start position, and sample ID.
mutation_id = paste0(
Hugo_Symbol, "_",
change, "_",
Start_Position, "_",
Tumor_Sample_Barcode
)
) %>%
# Get rid of any variables that have completely NAs.
dplyr::select(-which(apply(is.na(.), 2, all)))
}
Summarize and compare maf objects function
This function uses the maftools summary functions, maftools::getGeneSummary or maftools::getSampleSummary, to summarize and compare two maf objects. These maftools summary functions obtain the number of variants of each classification type on either a gene or sample level (this is taken from the information in the Variant_Classification field in the original maf file.) This field has the translation effect of the variant, e.g. “Frame_Shift_Del”.
correlate_maf_summaries <- function(maf1 = NULL, maf2 = NULL,
maf1_name = "maf1", maf2_name = "maf2",
summarize_by = "gene", cor_method = "pearson") {
# Takes two maf files, summarizes them by gene or sample, combines them by
# a full join, and correlates the summaries using Pearson's and Spearman's
# correlations.
#
# Args:
# maf1: a first maf object to summarize and compare to maf2
# maf2: a second maf object to summarize and compare to maf1
# maf1_name: a character string that indicates what label you would like to
# use for maf1's data.
# maf2_name: a character string that indicates what label you would like to
# use for maf2's data.
# summarize_by: a single character string signifying either "gene" or "sample"
# This will indicate whether to summarize compare by gene or sample.
# "gene" will summarize both maf1 and maf2 using maftools::getGeneSummary
# "sample" will summarize both maf1 and maf2 using maftools::getSampleSummary
# cor_method: a character string indicating the correlation method argument
# to be passed to "method" in cor.test() function
# Returns:
# a data.frame that contains the correlation r and p values for both
# maf1 and maf2 based on the numbers of variants of each class.
# Summarize the maf objects by the specified argument
if (summarize_by == "gene") {
maf1_sum <- maftools::getGeneSummary(maf1)
maf2_sum <- maftools::getGeneSummary(maf2)
key <- "Hugo_Symbol"
}
if (summarize_by == "sample") {
maf1_sum <- maftools::getSampleSummary(maf1)
maf2_sum <- maftools::getSampleSummary(maf2)
key <- "Tumor_Sample_Barcode"
}
# Do a full join of both summaries.
combine_df <- maf1_sum %>%
dplyr::full_join(maf2_sum, by = key) %>%
# Melt this data.frame so we can make it long format
reshape2::melt(id = key) %>%
dplyr::mutate(dataset = as.character(grepl(".x$", variable))) %>%
# Make a new column that specifies what maf object the data is from
dplyr::mutate(dataset = dplyr::recode(dataset,
`TRUE` = maf1_name,
`FALSE` = maf2_name,
)) %>%
# Gets rid of the ".x" and ".y" specifications of the column names
dplyr::mutate(variable = gsub(".x$|.y$", "", variable)) %>%
# Spreads the data based on the new dataset variable
tidyr::spread("dataset", "value")
# Get correlations by each type of variant classification
cors <- combine_df %>%
dplyr::group_by(variable) %>%
dplyr::summarize(cor = cor.test(
# This `eval(parse(text =` piece is so that cor.test will correlate the
# numeric vectors not the character strings themselves
eval(parse(text = maf1_name)),
eval(parse(text = maf2_name)),
method = cor.method)$estimate)
# Add the r values to the data labels
combine_df <- combine_df %>%
dplyr::mutate(variable_w_cor = paste(variable,
"r =",
round(cors$cor, 3)))
# Plot this as a facet_wrapped scatterplot
ggplot2::ggplot(combine_df, ggplot2::aes(x = eval(parse(text = maf1_name)),
y = eval(parse(text = maf2_name)))) +
ggplot2::geom_hex(bins = 10) +
ggplot2::facet_wrap(~ variable_w_cor, scales = "free") +
ggplot2::xlab(maf1_name) +
ggplot2::ylab(maf2_name) +
ggplot2::theme_classic() +
ggplot2::theme(strip.text = ggplot2::element_text(size = 8))
}
Read in the Strelka2 and Mutect2 data
We will read in the data as maftools objects from an RDS file, unless maftools has not been run on them yet. We will use metadata as the clinicalData for the maftools object.
Note: If you trying to run the initial set up step in a Docker container, it will likely be out of memory killed, unless you have ~50GB you can allocate to Docker.
# Get a vector of whether these exist
files_needed <- file.exists(metadata_dir, strelka2_dir, mutect2_dir)
if (all(files_needed)) {
# Read the ready-to-go files if these files exist
metadata <- metadata <- readr::read_tsv(metadata_dir)
strelka2 <- readRDS(strelka2_dir)
mutect2 <- readRDS(mutect2_dir)
} else { # If any of the needed files don't exist, rerun this process:
# Only import the sample names
strelka2_samples <- data.table::fread(file.path(
data_dir,
"pbta-snv-strelka2.vep.maf.gz"
),
select = "Tumor_Sample_Barcode",
skip = 1,
data.table = FALSE
) %>%
dplyr::pull("Tumor_Sample_Barcode")
mutect2_samples <- data.table::fread(file.path(
data_dir,
"pbta-snv-mutect2.vep.maf.gz"
),
select = "Tumor_Sample_Barcode",
skip = 1,
data.table = FALSE
) %>%
dplyr::pull("Tumor_Sample_Barcode")
# Isolate metadata to only the samples that are in the datasets
metadata <- readr::read_tsv(file.path(data_dir, "pbta-histologies.tsv")) %>%
dplyr::filter(Kids_First_Biospecimen_ID %in% c(strelka2_samples, mutect2_samples)) %>%
dplyr::distinct(Kids_First_Biospecimen_ID, .keep_all = TRUE) %>%
dplyr::arrange() %>%
dplyr::rename(Tumor_Sample_Barcode = Kids_First_Biospecimen_ID) %>%
readr::write_tsv(file.path(scratch_dir, "metadata_filtered_maf_samples.tsv"))
# Read in original strelka file with maftools
strelka2 <- maftools::read.maf(file.path(data_dir, "pbta-snv-strelka2.vep.maf.gz"),
clinicalData = metadata
)
# Save to RDS so we don't have to run this again
saveRDS(strelka2, strelka2_dir)
# Same for MuTect2
mutect2 <- maftools::read.maf(file.path(data_dir, "pbta-snv-mutect2.vep.maf.gz"),
clinicalData = metadata
)
saveRDS(mutect2, mutect2_dir)
}
Parsed with column specification:
cols(
.default = col_character(),
age_at_diagnosis = [32mcol_double()[39m,
molecular_subtype = [33mcol_logical()[39m
)
See spec(...) for full column specifications.
Compare number of mutations per gene
We will our pre-made function, correlate_maf_summaries, to evaluate initial correlations between the algorithms on the gene-level. These correlations across variant classifications are plotted as a barplot.
correlate_maf_summaries(maf1 = strelka2,
maf2 = mutect2,
maf1_name = "strelka2",
maf2_name = "mutect2",
summarize_by = "gene",
cor_method = "pearson")

Compare number of mutations per sample
Similar to above, we will use our pre-made function, correlate_maf_summaries, to evaluate correlations between the algorithms on the sample-level. These correlations across variant classifications are plotted as a barplot.
correlate_maf_summaries(maf1 = strelka2,
maf2 = mutect2,
maf1_name = "strelka2",
maf2_name = "mutect2",
summarize_by = "sample",
cor_method = "pearson")

Plot Transition/Transversions
These built in maftools functions plot the overall presence of transitions and transversions and is recommended by the maftools vignette as a method of obtaining an overall base change summary.
Strelka2 transition/transversion plot.
maftools::plotTiTv(maftools::titv(strelka2))


MuTect2 transition/transversion plot.
maftools::plotTiTv(maftools::titv(mutect2))


Set up new variables
Let’s set up Strelka2’s variables first.
# Use the premade function to create our new variables
strelka2_vaf <- set_up_variables(strelka2)
NAs introduced by coercion
# Take a look at this df
strelka2_vaf
Now we will do the same for MuTect2.
# Use the premade function to create our new variables
mutect2_vaf <- set_up_variables(mutect2)
# Take a look at this df
mutect2_vaf
Combine MuTect2 and Strelka2 data.frames into one data.frame
Join these datasets based on the mutation_id created by the set_up_variables function. Save to a TSV file to be used in the subsequent notebook.
# Merge these data.frames together
vaf_df <- strelka2_vaf %>%
dplyr::full_join(mutect2_vaf,
by = "mutation_id",
suffix = c(".strelka2", ".mutect2")
) %>%
# Make a variable that denotes which dataset it is in.
dplyr::mutate(dataset = dplyr::case_when(
is.na(Allele.mutect2) ~ "strelka2_only",
is.na(Allele.strelka2) ~ "mutect2_only",
TRUE ~ "both"
)) %>%
readr::write_tsv(file.path("results", "combined_results.tsv"))
Make a zipped up version that can be stored on GitHub.
zip(
file.path("results", "combined_results.tsv.zip"),
file.path("results", "combined_results.tsv")
)
updating: results/combined_results.tsv (deflated 85%)
Session Info:
sessionInfo()
R version 3.6.1 (2019-07-05)
Platform: x86_64-apple-darwin15.6.0 (64-bit)
Running under: macOS Mojave 10.14.5
Matrix products: default
BLAS: /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libBLAS.dylib
LAPACK: /Library/Frameworks/R.framework/Versions/3.6/Resources/lib/libRlapack.dylib
locale:
[1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8
attached base packages:
[1] parallel stats graphics grDevices utils datasets methods base
other attached packages:
[1] Biobase_2.44.0 BiocGenerics_0.30.0
loaded via a namespace (and not attached):
[1] pkgload_1.0.2 tidyr_0.8.3 jsonlite_1.6 splines_3.6.1 foreach_1.4.7 assertthat_0.2.1 BiocManager_1.30.4
[8] yaml_2.2.0 remotes_2.1.0 sessioninfo_1.1.1 pillar_1.4.2 backports_1.1.4 lattice_0.20-38 glue_1.3.1
[15] digest_0.6.20 RColorBrewer_1.1-2 colorspace_1.4-1 htmltools_0.3.6 Matrix_1.2-17 plyr_1.8.4 pkgconfig_2.0.2
[22] devtools_2.1.0 bibtex_0.4.2 purrr_0.3.2 xtable_1.8-4 scales_1.0.0 processx_3.4.1 tibble_2.1.3
[29] pkgmaker_0.27 ggplot2_3.2.1 usethis_1.5.1 withr_2.1.2 hexbin_1.27.3 lazyeval_0.2.2 cli_1.1.0
[36] survival_2.44-1.1 magrittr_1.5 crayon_1.3.4 memoise_1.1.0 evaluate_0.14 ps_1.3.0 fs_1.3.1
[43] doParallel_1.0.15 NMF_0.21.0 pkgbuild_1.0.4 tools_3.6.1 registry_0.5-1 data.table_1.12.2 prettyunits_1.0.2
[50] hms_0.5.0 gridBase_0.4-7 stringr_1.4.0 munsell_0.5.0 cluster_2.1.0 rngtools_1.4 maftools_2.0.15
[57] callr_3.3.1 compiler_3.6.1 rlang_0.4.0 grid_3.6.1 iterators_1.0.12 rstudioapi_0.10 base64enc_0.1-3
[64] labeling_0.3 rmarkdown_1.14 testthat_2.2.1 gtable_0.3.0 codetools_0.2-16 reshape2_1.4.3 R6_2.4.0
[71] knitr_1.24 dplyr_0.8.3 zeallot_0.1.0 rprojroot_1.3-2 readr_1.3.1 desc_1.2.0 stringi_1.4.3
[78] Rcpp_1.0.2 vctrs_0.2.0 wordcloud_2.6 tidyselect_0.2.5 xfun_0.8
LS0tCnRpdGxlOiAiU2V0IHVwIGNvbWJpbmVkIGRhdGEgb2YgTXV0ZWN0MiBhbmQgU3RyZWxrYTIiCm91dHB1dDogICAKICBodG1sX25vdGVib29rOiAKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiB0cnVlCi0tLQoKQ2FuZGFjZSBTYXZvbmVuIC0gQ0NETCBmb3IgQUxTRgoKVGhpcyBub3RlYm9vayBzZXRzIHVwIHRoZSBbTUFGIGRhdGEgZmlsZXMgYXMgYSBjb21iaW5lZCBmaWxlIGZvciByZWFkeSBjb21wYXJpc29uIGluIDAyLWFuYWx5emUtY29uY29yZGFuY2UuUm1kXShodHRwczovL2dpdGh1Yi5jb20vQWxleHNMZW1vbmFkZS9PcGVuUEJUQS1hbmFseXNpcy90cmVlL21hc3Rlci9hbmFseXNlcy9tdXRlY3QyLXZzLXN0cmVsa2EyLzAyLWFuYWx5emUtY29uY29yZGFuY2UuUm1kKQphbmQgYWxzbyBkb2VzIHNvbWUgZmlyc3QgbGluZSBhbmFseXNlcyBmcm9tIHJlYWR5LW1hZGUgYG1hZnRvb2xzYCBmdW5jdGlvbnMuCgpJdCBpcyB0aGUgZmlyc3Qgbm90ZWJvb2sgaW4gdGhpcyBzZXJpZXMgd2hpY2ggYWRkcmVzc2VzIFtpc3N1ZSBcIyAzMCBpbiBPcGVuUEJUQV0oaHR0cHM6Ly9naXRodWIuY29tL0FsZXhzTGVtb25hZGUvT3BlblBCVEEtYW5hbHlzaXMvaXNzdWVzLzMwKS4KCiMjIyBTdW1tYXJ5IG9mIHRoZSBTZXQgVXA6ICAKCkZvciBib3RoIHRoZSBTdHJlbGthMiBhbmQgTXVUZWN0MiBkYXRhc2V0cywgdGhlIGRhdGEgaXMgaW1wb3J0ZWQgYnkgCmBtYWZ0b29sczo6cmVhZC5tYWZgIGFuZCB0aGUgY29ycmVzcG9uZGluZyBjbGluaWNhbCBkYXRhIGZyb20gCmBwYnRhLWhpc3RvbG9naWVzLnRzdmAgaXMgYWRkZWQgdG8gdGhlIG9iamVjdC4gClRoaXMgaXMgb25seSBkb25lIG9uY2UgYXMgd3JpdHRlbiAoYXMgdGhlIGByZWFkLm1hZmAgaXMgdmVyeSBtZW1vcnkgaW50ZW5zaXZlKQphbmQgZWFjaCBNdVRlY3QyIGFuZCBTdHJlbGthMiBhcmUgc2F2ZWQgdG8gYW4gUkRTIGZpbGUgZm9yIGZhc3RlciBhbmQgcmVhZHkgCnJlbG9hZGluZy4KCkZvciBib3RoIFN0cmVsa2EyIGFuZCBNdVRlY3QyIGRhdGEsIHRoZSBmb2xsb3dpbmcgdmFyaWFibGVzIGFyZSBjYWxjdWxhdGVkCmFuZCBhZGRlZCBpbnRvIHRoZSBjb21iaW5lZCBkYXRhc2V0IGZvciBhbmFseXNpcyBpbiBbMDItYW5hbHl6ZS1jb25jb3JkYW5jZS5SbWRdKGh0dHBzOi8vZ2l0aHViLmNvbS9BbGV4c0xlbW9uYWRlL09wZW5QQlRBLWFuYWx5c2lzL3RyZWUvbWFzdGVyL2FuYWx5c2VzL211dGVjdDItdnMtc3RyZWxrYTIvMDItYW5hbHl6ZS1jb25jb3JkYW5jZS5SbWQpLgoKIyMjIyBWYXJpYWJsZXMgY3JlYXRlZDogIAoKLSBgdmFmYCA6IFZhcmlhbnQgQWxsZWxlIEZyZXF1ZW5jeSA9IGAodF9hbHRfY291bnQpIC8gKHRfcmVmX2NvdW50ICsgdF9hbHRfY291bnQpYC4gIAotIGBtdXRhdGlvbl9pZGA6IFVzZWQgdG8gZGV0ZXJtaW5lIHdoZXRoZXIgdHdvIHZhcmlhbnRzIGNhbGxzIGFyZSBpZGVudGljYWwgCmluIGJvdGggZGF0YXNldHMuIApJdCBpcyBhIGNvbmNhdGVuYXRpb24gb2Y6IGBIdWdvX1N5bWJvbGAsIGBjaGFuZ2VgLCBgU3RhcnRfUG9zaXRpb25gLCBhbmQgCmBUdW1vcl9TYW1wbGVfQmFyY29kZWAgKHRoZSBzYW1wbGUgSUQpLiAgIAotIGBiYXNlX2NoYW5nZWA6IHZhcmlhYmxlIHRoYXQgaW5kaWNhdGVzIHRoZSBleGFjdCBjaGFuZ2UgaW4gYmFzZXMgKGUuZy4gJ1Q+QycpLiAgCi0gYGNoYW5nZWA6IGluZGljYXRlcyB0aGUgYGJhc2VfY2hhbmdlYCBpbmZvcm1hdGlvbiBidXQgZ3JvdXBzIHRvZ2V0aGVyIApkZWxldGlvbnMsIGluc2VydGlvbnMsIGFuZCBsb25nIChtb3JlIHRoYW4gYSBTTlYpIGFzIHRoZWlyIG93biBncm91cHMuICAKLSBgY29kaW5nYDogU3VtbWFyaXplIHRoZSBgQklPVFlQRWAgdmFyaWFibGUgZm9yIHdoZXRoZXIgb3Igbm90IGl0IGlzIGEgY29kaW5nIGdlbmUuICAKCiMjIyMgVGhlIG91dHB1dCBmaWxlcyBmcm9tIHRoaXMgbm90ZWJvb2s6CgotIGBzY3JhdGNoL3N0cmVsa2EyLlJEU2AgIAotIGBzY3JhdGNoL211dGVjdDIuUkRTYCAgCi0gYHNjcmF0Y2gvbWV0YWRhdGFfZmlsdGVyZWRfbWFmX3NhbXBsZXMudHN2YCAgCi0gYGFuYWx5c2VzL211dGVjdDItdnMtc3RyZWxrYTIvcGxvdHMvc2FtcGxlX2Nvcl9tdXRlY3QyX3ZzX3N0cmVsa2EyLnBkZmAKLSBgYW5hbHlzZXMvbXV0ZWN0Mi12cy1zdHJlbGthMi9wbG90cy9zYW1wbGVfY29yX211dGVjdDJfdnNfc3RyZWxrYTIucGRmYAotIGBhbmFseXNlcy9tdXRlY3QyLXZzLXN0cmVsa2EyL3Jlc3VsdHMvY29tYmluZWRfcmVzdWx0cy50c3ZgICAKCiMjIFVzYWdlCgpUbyBydW4gdGhpcyBmcm9tIHRoZSBjb21tYW5kIGxpbmUsIHVzZToKYGBgClJzY3JpcHQgLWUgInJtYXJrZG93bjo6cmVuZGVyKCdhbmFseXNlcy9tdXRlY3QyLXZzLXN0cmVsa2EyLzAxLXNldC11cC5SbWQnLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2xlYW4gPSBUUlVFKSIKYGBgCiBfVGhpcyBhc3N1bWVzIHlvdSBhcmUgaW4gdGhlIHRvcCBkaXJlY3Rvcnkgb2YgdGhlIHJlcG9zaXRvcnkuXwoKIyMgU2V0IFVwCgpgYGB7cn0KIyBXZSBuZWVkIG1hZnRvb2xzIC0gdGhpcyB3aWxsIGJlIGFkZGVkIHRvIHRoZSBydW5uaW5nIERvY2tlciBpc3N1ZSB3aGVuZXZlciBpdCBpcyB1cAppZiAoISgibWFmdG9vbHMiICVpbiUgaW5zdGFsbGVkLnBhY2thZ2VzKCkpKSB7CiAgaWYgKCFyZXF1aXJlTmFtZXNwYWNlKCJCaW9jTWFuYWdlciIsIHF1aWV0bHkgPSBUUlVFKSkgewogICAgaW5zdGFsbC5wYWNrYWdlcygiQmlvY01hbmFnZXIiKQogIH0KICBCaW9jTWFuYWdlcjo6aW5zdGFsbCgibWFmdG9vbHMiKQp9CgojIFdpbGwgbmVlZCBoZXhiaW4gZm9yIHRoZSBoZXggcGxvdAppZiAoISgiaGV4YmluIiAlaW4lIGluc3RhbGxlZC5wYWNrYWdlcygpKSkgewogIGluc3RhbGwucGFja2FnZXMoImhleGJpbiIpCn0KYGBgCgpHZXQgYG1hZ3JpdHRyYCBwaXBlCgpgYGB7cn0KYCU+JWAgPC0gZHBseXI6OmAlPiVgCmBgYAoKIyMjIERpcmVjdG9yaWVzIGFuZCBmaWxlcwoKUGF0aCB0byB0aGUgc3ltbGlua2VkIGRhdGEgb2J0YWluZWQgdmlhIGBiYXNoIGRvd25sb2FkLWRhdGEuc2hgLgoKYGBge3J9CmRhdGFfZGlyIDwtIGZpbGUucGF0aCgiLi4iLCAiLi4iLCAiZGF0YSIpCnNjcmF0Y2hfZGlyIDwtIGZpbGUucGF0aCgiLi4iLCAiLi4iLCAic2NyYXRjaCIpCmBgYAoKQ3JlYXRlIG91dHB1dCBkaXJlY3RvcmllcyBpbiB0aGlzIGFuYWx5c2lzIGZvbGRlci4KCmBgYHtyfQppZiAoIWRpci5leGlzdHMoInJlc3VsdHMiKSkgewogIGRpci5jcmVhdGUoInJlc3VsdHMiKQp9CmlmICghZGlyLmV4aXN0cygicGxvdHMiKSkgewogIGRpci5jcmVhdGUoInBsb3RzIikKfQpgYGAKCiMjIyBGdW5jdGlvbnMKClNldCB1cCB0aGUgZnVuY3Rpb24gdGhhdCB3aWxsIGNyZWF0ZSB0aGUgbmV3IHZhcmlhYmxlcyBmcm9tIHRoZSBtYWYgb2JqZWN0cy4gCgojIyMjIERlc2NyaXB0aW9uIG9mIHZhcmlhYmxlcyAKClRoZSBmdW5jdGlvbiwgYHNldF91cF92YXJpYWJsZXNgLCB3aWxsIGNhbGN1bGF0ZSB0aGUgZm9sbG93aW5nIHZhcmlhYmxlcyBmcm9tIGEgbWFmIG9iamVjdDogIAotIENhbGN1bGF0ZSBWQUYgZm9yIGVhY2guIApUaGUgVkFGcyBmb3IgZWFjaCB2YXJpYXRpb24gaW4gZWFjaCBkYXRhc2V0IGFyZSBjYWxjdWxhdGVkIGJ5IApgdF9hbHRfY291bnQpIC8gKHRfcmVmX2NvdW50ICsgdF9hbHRfY291bnQpIGAsIGFzIGZvbGxvd2luZyB0aGUgW2NvZGUgdXNlZCBpbiAKYG1hZnRvb2xzYF0oaHR0cHM6Ly9naXRodWIuY29tL1BvaXNvbkFsaWVuL21hZnRvb2xzL2Jsb2IvMWQwMjcwZTM1YzJlMGY0OTMwOWViYTA4YjYyMzQzYWMwZGIxMDU2MC9SL3Bsb3RfdmFmLlIjTDM5KSAgCi0gQ3JlYXRlIGEgYGJhc2VfY2hhbmdlYCB2YXJpYWJsZSB0aGF0IGluZGljYXRlcyB0aGUgZXhhY3QgY2hhbmdlIGluIGJhc2VzLiAgCi0gQ3JlYXRlIGEgYGNoYW5nZWAgdmFyaWFibGUgZm9yIGVhY2ggd2hpY2ggaW5kaWNhdGVzIHRoZSBgYmFzZV9jaGFuZ2VgCmluZm9ybWF0aW9uIGJ1dCBncm91cHMgdG9nZXRoZXIgZGVsZXRpb25zLCBpbnNlcnRpb25zLCBhbmQgbG9uZyAobW9yZSB0aGFuIGEgClNOVikgYXMgdGhlaXIgb3duIGdyb3Vwcy4gIAotIE1ha2UgYSBtdXRhdGlvbiBpZCBieSBjb25jYXRlbmF0aW5nIGBIdWdvX1N5bWJvbGAsIGBjaGFuZ2VgLCBgU3RhcnRfUG9zaXRpb25gLAphbmQgYFR1bW9yX1NhbXBsZV9CYXJjb2RlYCAodGhlIHNhbXBsZSBJRCkuICAgCldlIHdpbGwgdXNlIHRoaXMgdmFyaWFibGUgdG8gZGV0ZXJtaW5lIHdoZXRoZXIgdHdvIHZhcmlhbnRzIGNhbGxzIGFyZSBpZGVudGljYWwgCmluIGJvdGggZGF0YXNldHMuIAotIFN1bW1hcml6ZSB0aGUgYEJJT1RZUEVgIHZhcmlhYmxlIGZvciB3aGV0aGVyIG9yIG5vdCBpdCBpcyBhIGNvZGluZyBnZW5lLiAgIAoKYGBge3J9CnNldF91cF92YXJpYWJsZXMgPC0gZnVuY3Rpb24obWFmID0gTlVMTCkgewogICMgQ3JlYXRlcyB0aGVzZSBuZXcgdmFyaWFibGVzIGZyb20gYSBtYWYgb2JqZWN0IHByb3ZpZGVkOiBWQUYsIG11dGF0aW9uX2lkLCAKICAjIGJhc2VfY2hhbmdlLCBjaGFuZ2UsIGNvZGluZy4KICAjCiAgIyBBcmdzOgogICMgICBtYWY6IGEgbWFmIG9iamVjdCB0byBjcmVhdGUgbmV3IHZhcmlhYmxlcyBmcm9tCiAgIwogICMgUmV0dXJuczoKICAjICAgYSBkYXRhLmZyYW1lIHdpdGggYWxsIHRoZSBvcmlnaW5hbCBpbmZvcm1hdGlvbiBpbiB0aGUgYEBkYXRhYCBwYXJ0IG9mIHRoZSAKICAjICAgbWFmIG9iamVjdCBidXQgd2l0aCB0aGVzZSBuZXcgdmFyaWFibGVzOiBWQUYsIG11dGF0aW9uX2lkLCBiYXNlX2NoYW5nZSwgCiAgIyAgIGNoYW5nZSwgY29kaW5nLgoKICAjIEV4dHJhY3QgdGhlIGRhdGEgcGFydCBvZiB0aGUgbWFmIG9iamVjdCwgcHV0IGl0IHRocm91Z2ggYSBkcGx5ciBwaXBlLiAKICBkZiA8LSBtYWZAZGF0YQogIGRmICU+JQogICAgZHBseXI6Om11dGF0ZSgKICAgICAgIyBDYWxjdWxhdGUgdGhlIHZhcmlhbnQgYWxsZWxlIGZyZXF1ZW5jeQogICAgICB2YWYgPSBhcy5udW1lcmljKHRfYWx0X2NvdW50KSAvIChhcy5udW1lcmljKHRfcmVmX2NvdW50KSArCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYXMubnVtZXJpYyh0X2FsdF9jb3VudCkpLAogICAgICAjIENyZWF0ZSBhIGJhc2VfY2hhbmdlIHZhcmlhYmxlCiAgICAgIGJhc2VfY2hhbmdlID0gcGFzdGUwKFJlZmVyZW5jZV9BbGxlbGUsICI+IiwgQWxsZWxlKSwKICAgICAgCiAgICAgICMgQ3JlYXRlIGEgdmFyaWFibGUgdGhhdCBub3RlcyB3aGV0aGVyIHRoZSB2YXJpYW50IGlzIGluIGEgCiAgICAgICMgY29kaW5nL25vbi1jb2RpbmcgcmVnaW9uCiAgICAgIGNvZGluZyA9IGRwbHlyOjpjYXNlX3doZW4oCiAgICAgICAgQklPVFlQRSAhPSAicHJvdGVpbl9jb2RpbmciIH4gIm5vbi1jb2RpbmciLAogICAgICAgIFRSVUUgfiAicHJvdGVpbl9jb2RpbmciCiAgICAgICkKICAgICkgJT4lCiAgICBkcGx5cjo6bXV0YXRlKAogICAgICAjIEZyb20gdGhlIGJhc2VfY2hhbmdlIHZhcmlhYmxlLCBzdW1tYXJpemUgaW5zZXJ0aW9ucywgZGVsZXRpb25zLCBhbmQgCiAgICAgICMgY2hhbmdlcyB0aGF0IGFyZSBtb3JlIHRoYW4gb25lIGJhc2UgaW50byB0aGVpciBvd24gZ3JvdXBzLgogICAgICBjaGFuZ2UgPSBkcGx5cjo6Y2FzZV93aGVuKAogICAgICBncmVwbCgiXi0iLCBiYXNlX2NoYW5nZSkgfiAiaW5zZXJ0aW9uIiwKICAgICAgZ3JlcGwoIi0kIiwgYmFzZV9jaGFuZ2UpIH4gImRlbGV0aW9uIiwKICAgICAgbmNoYXIoYmFzZV9jaGFuZ2UpID4gMyB+ICJsb25nX2NoYW5nZSIsCiAgICAgIFRSVUUgfiBiYXNlX2NoYW5nZQogICAgKSkgJT4lCiAgICBkcGx5cjo6bXV0YXRlKAogICAgICAjIENyZWF0ZSB0aGUgbXV0YXRpb24gaWQgYmFzZWQgb24gdGhlIGNoYW5nZSB2YXJpYWJsZSBhcyB3ZWxsIGFzIHRoZSAKICAgICAgIyBnZW5lIHN5bWJvbCwgc3RhcnQgcG9zaXRpb24sIGFuZCBzYW1wbGUgSUQuIAogICAgICBtdXRhdGlvbl9pZCA9IHBhc3RlMCgKICAgICAgICBIdWdvX1N5bWJvbCwgIl8iLAogICAgICAgIGNoYW5nZSwgIl8iLAogICAgICAgIFN0YXJ0X1Bvc2l0aW9uLCAiXyIsCiAgICAgICAgVHVtb3JfU2FtcGxlX0JhcmNvZGUKICAgICAgKQogICAgKSAlPiUKICAgICMgR2V0IHJpZCBvZiBhbnkgdmFyaWFibGVzIHRoYXQgaGF2ZSBjb21wbGV0ZWx5IE5Bcy4KICAgIGRwbHlyOjpzZWxlY3QoLXdoaWNoKGFwcGx5KGlzLm5hKC4pLCAyLCBhbGwpKSkKfQpgYGAKCiMjIyMgU3VtbWFyaXplIGFuZCBjb21wYXJlIGBtYWZgIG9iamVjdHMgZnVuY3Rpb24gCgpUaGlzIGZ1bmN0aW9uIHVzZXMgdGhlIG1hZnRvb2xzIHN1bW1hcnkgZnVuY3Rpb25zLCBgbWFmdG9vbHM6OmdldEdlbmVTdW1tYXJ5YCBvcgpgbWFmdG9vbHM6OmdldFNhbXBsZVN1bW1hcnlgLCB0byBzdW1tYXJpemUgYW5kIGNvbXBhcmUgdHdvIG1hZiBvYmplY3RzLgpUaGVzZSBgbWFmdG9vbHNgIHN1bW1hcnkgZnVuY3Rpb25zIG9idGFpbiB0aGUgbnVtYmVyIG9mIHZhcmlhbnRzIG9mIGVhY2ggCmNsYXNzaWZpY2F0aW9uIHR5cGUgb24gZWl0aGVyIGEgZ2VuZSBvciBzYW1wbGUgbGV2ZWwgKHRoaXMgaXMgdGFrZW4gZnJvbSB0aGUgCmluZm9ybWF0aW9uIGluIHRoZSBbYFZhcmlhbnRfQ2xhc3NpZmljYXRpb25gIGZpZWxkIGluIHRoZSBvcmlnaW5hbCBtYWYgZmlsZV0oaHR0cHM6Ly9kb2NzLmdkYy5jYW5jZXIuZ292L0RhdGEvRmlsZV9Gb3JtYXRzL01BRl9Gb3JtYXQvKS4pClRoaXMgZmllbGQgaGFzIHRoZSB0cmFuc2xhdGlvbiBlZmZlY3Qgb2YgdGhlIHZhcmlhbnQsIGUuZy4gIkZyYW1lX1NoaWZ0X0RlbCIuCgpgYGB7cn0KY29ycmVsYXRlX21hZl9zdW1tYXJpZXMgPC0gZnVuY3Rpb24obWFmMSA9IE5VTEwsIG1hZjIgPSBOVUxMLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWFmMV9uYW1lID0gIm1hZjEiLCBtYWYyX25hbWUgPSAibWFmMiIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdW1tYXJpemVfYnkgPSAiZ2VuZSIsIGNvcl9tZXRob2QgPSAicGVhcnNvbiIpIHsKICAjIFRha2VzIHR3byBtYWYgZmlsZXMsIHN1bW1hcml6ZXMgdGhlbSBieSBnZW5lIG9yIHNhbXBsZSwgY29tYmluZXMgdGhlbSBieSAKICAjIGEgZnVsbCBqb2luLCBhbmQgY29ycmVsYXRlcyB0aGUgc3VtbWFyaWVzIHVzaW5nIFBlYXJzb24ncyBhbmQgU3BlYXJtYW4ncwogICMgY29ycmVsYXRpb25zLiAKICAjCiAgIyBBcmdzOgogICMgICBtYWYxOiBhIGZpcnN0IG1hZiBvYmplY3QgdG8gc3VtbWFyaXplIGFuZCBjb21wYXJlIHRvIG1hZjIKICAjICAgbWFmMjogYSBzZWNvbmQgbWFmIG9iamVjdCB0byBzdW1tYXJpemUgYW5kIGNvbXBhcmUgdG8gbWFmMQogICMgICBtYWYxX25hbWU6IGEgY2hhcmFjdGVyIHN0cmluZyB0aGF0IGluZGljYXRlcyB3aGF0IGxhYmVsIHlvdSB3b3VsZCBsaWtlIHRvIAogICMgICB1c2UgZm9yIG1hZjEncyBkYXRhLiAKICAjICAgbWFmMl9uYW1lOiBhIGNoYXJhY3RlciBzdHJpbmcgdGhhdCBpbmRpY2F0ZXMgd2hhdCBsYWJlbCB5b3Ugd291bGQgbGlrZSB0byAKICAjICAgdXNlIGZvciBtYWYyJ3MgZGF0YS4gCiAgIyAgIHN1bW1hcml6ZV9ieTogYSBzaW5nbGUgY2hhcmFjdGVyIHN0cmluZyBzaWduaWZ5aW5nIGVpdGhlciAiZ2VuZSIgb3IgInNhbXBsZSIKICAjICAgICAgIFRoaXMgd2lsbCBpbmRpY2F0ZSB3aGV0aGVyIHRvIHN1bW1hcml6ZSBjb21wYXJlIGJ5IGdlbmUgb3Igc2FtcGxlLiAKICAjICAgICAgICJnZW5lIiB3aWxsIHN1bW1hcml6ZSBib3RoIG1hZjEgYW5kIG1hZjIgdXNpbmcgbWFmdG9vbHM6OmdldEdlbmVTdW1tYXJ5CiAgIyAgICAgICAic2FtcGxlIiB3aWxsIHN1bW1hcml6ZSBib3RoIG1hZjEgYW5kIG1hZjIgdXNpbmcgbWFmdG9vbHM6OmdldFNhbXBsZVN1bW1hcnkKICAjICAgY29yX21ldGhvZDogYSBjaGFyYWN0ZXIgc3RyaW5nIGluZGljYXRpbmcgdGhlIGNvcnJlbGF0aW9uIG1ldGhvZCBhcmd1bWVudCAKICAjICAgICAgICAgICAgICAgdG8gYmUgcGFzc2VkIHRvICJtZXRob2QiIGluIGNvci50ZXN0KCkgZnVuY3Rpb24gCiAgIyBSZXR1cm5zOgogICMgICBhIGRhdGEuZnJhbWUgdGhhdCBjb250YWlucyB0aGUgY29ycmVsYXRpb24gciBhbmQgcCB2YWx1ZXMgZm9yIGJvdGggCiAgIyAgIG1hZjEgYW5kIG1hZjIgYmFzZWQgb24gdGhlIG51bWJlcnMgb2YgdmFyaWFudHMgb2YgZWFjaCBjbGFzcy4KICAKICAjIFN1bW1hcml6ZSB0aGUgbWFmIG9iamVjdHMgYnkgdGhlIHNwZWNpZmllZCBhcmd1bWVudAogIGlmIChzdW1tYXJpemVfYnkgPT0gImdlbmUiKSB7CiAgICBtYWYxX3N1bSA8LSBtYWZ0b29sczo6Z2V0R2VuZVN1bW1hcnkobWFmMSkKICAgIG1hZjJfc3VtIDwtIG1hZnRvb2xzOjpnZXRHZW5lU3VtbWFyeShtYWYyKQogICAga2V5IDwtICJIdWdvX1N5bWJvbCIKICB9CiAgaWYgKHN1bW1hcml6ZV9ieSA9PSAic2FtcGxlIikgewogICAgbWFmMV9zdW0gPC0gbWFmdG9vbHM6OmdldFNhbXBsZVN1bW1hcnkobWFmMSkKICAgIG1hZjJfc3VtIDwtIG1hZnRvb2xzOjpnZXRTYW1wbGVTdW1tYXJ5KG1hZjIpCiAgICBrZXkgPC0gIlR1bW9yX1NhbXBsZV9CYXJjb2RlIgogIH0KICAKICAjIERvIGEgZnVsbCBqb2luIG9mIGJvdGggc3VtbWFyaWVzLgogIGNvbWJpbmVfZGYgPC0gbWFmMV9zdW0gJT4lCiAgICBkcGx5cjo6ZnVsbF9qb2luKG1hZjJfc3VtLCBieSA9IGtleSkgJT4lCiAgICAjIE1lbHQgdGhpcyBkYXRhLmZyYW1lIHNvIHdlIGNhbiBtYWtlIGl0IGxvbmcgZm9ybWF0CiAgICByZXNoYXBlMjo6bWVsdChpZCA9IGtleSkgJT4lCiAgICBkcGx5cjo6bXV0YXRlKGRhdGFzZXQgPSBhcy5jaGFyYWN0ZXIoZ3JlcGwoIi54JCIsIHZhcmlhYmxlKSkpICU+JQogICAgIyBNYWtlIGEgbmV3IGNvbHVtbiB0aGF0IHNwZWNpZmllcyB3aGF0IG1hZiBvYmplY3QgdGhlIGRhdGEgaXMgZnJvbQogICAgZHBseXI6Om11dGF0ZShkYXRhc2V0ID0gZHBseXI6OnJlY29kZShkYXRhc2V0LAogICAgICBgVFJVRWAgPSBtYWYxX25hbWUsCiAgICAgIGBGQUxTRWAgPSBtYWYyX25hbWUsCiAgICApKSAlPiUKICAgICMgR2V0cyByaWQgb2YgdGhlICIueCIgYW5kICIueSIgc3BlY2lmaWNhdGlvbnMgb2YgdGhlIGNvbHVtbiBuYW1lcwogICAgZHBseXI6Om11dGF0ZSh2YXJpYWJsZSA9IGdzdWIoIi54JHwueSQiLCAiIiwgdmFyaWFibGUpKSAlPiUKICAgICMgU3ByZWFkcyB0aGUgZGF0YSBiYXNlZCBvbiB0aGUgbmV3IGRhdGFzZXQgdmFyaWFibGUKICAgIHRpZHlyOjpzcHJlYWQoImRhdGFzZXQiLCAidmFsdWUiKSAgIAogICAgCiAgIyBHZXQgY29ycmVsYXRpb25zIGJ5IGVhY2ggdHlwZSBvZiB2YXJpYW50IGNsYXNzaWZpY2F0aW9uCiAgY29ycyA8LSBjb21iaW5lX2RmICU+JQogICAgZHBseXI6Omdyb3VwX2J5KHZhcmlhYmxlKSAlPiUKICAgIGRwbHlyOjpzdW1tYXJpemUoY29yID0gY29yLnRlc3QoCiAgICAgICMgVGhpcyBgZXZhbChwYXJzZSh0ZXh0ID1gIHBpZWNlIGlzIHNvIHRoYXQgY29yLnRlc3Qgd2lsbCBjb3JyZWxhdGUgdGhlIAogICAgICAjIG51bWVyaWMgdmVjdG9ycyBub3QgdGhlIGNoYXJhY3RlciBzdHJpbmdzIHRoZW1zZWx2ZXMgCiAgICAgIGV2YWwocGFyc2UodGV4dCA9IG1hZjFfbmFtZSkpLCAKICAgICAgZXZhbChwYXJzZSh0ZXh0ID0gbWFmMl9uYW1lKSksCiAgICAgIG1ldGhvZCA9IGNvci5tZXRob2QpJGVzdGltYXRlKSAKICAKICAjIEFkZCB0aGUgciB2YWx1ZXMgdG8gdGhlIGRhdGEgbGFiZWxzCiAgY29tYmluZV9kZiA8LSBjb21iaW5lX2RmICU+JQogICAgICBkcGx5cjo6bXV0YXRlKHZhcmlhYmxlX3dfY29yID0gcGFzdGUodmFyaWFibGUsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyID0iLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByb3VuZChjb3JzJGNvciwgMykpKQogIAogICMgUGxvdCB0aGlzIGFzIGEgZmFjZXRfd3JhcHBlZCBzY2F0dGVycGxvdAogIGdncGxvdDI6OmdncGxvdChjb21iaW5lX2RmLCBnZ3Bsb3QyOjphZXMoeCA9IGV2YWwocGFyc2UodGV4dCA9IG1hZjFfbmFtZSkpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeSA9ICBldmFsKHBhcnNlKHRleHQgPSBtYWYyX25hbWUpKSkpICsKICAgIGdncGxvdDI6Omdlb21faGV4KGJpbnMgPSAxMCkgKyAKICAgIGdncGxvdDI6OmZhY2V0X3dyYXAofiB2YXJpYWJsZV93X2Nvciwgc2NhbGVzID0gImZyZWUiKSArCiAgICBnZ3Bsb3QyOjp4bGFiKG1hZjFfbmFtZSkgKyAKICAgIGdncGxvdDI6OnlsYWIobWFmMl9uYW1lKSArCiAgICBnZ3Bsb3QyOjp0aGVtZV9jbGFzc2ljKCkgKwogICAgZ2dwbG90Mjo6dGhlbWUoc3RyaXAudGV4dCA9IGdncGxvdDI6OmVsZW1lbnRfdGV4dChzaXplID0gOCkpCn0KYGBgCgoKIyMgUmVhZCBpbiB0aGUgbWV0YWRhdGEgaW5mb3JtYXRpb24KClJ1bm5pbmcgYG1hZnRvb2xzOjpyZWFkLm1hZmAgdGFrZXMgYSBsb3Qgb2YgY29tcHV0aW5nIHBvd2VyIGFuZCB0aW1lLCBzbyB0byAKYXZvaWQgaGF2aW5nIHRvIHJ1biB0aGlzIGZvciBib3RoIGRhdGFzZXRzIGV2ZXJ5dGltZSB3ZSB3YW50IHRvIHJlLXJ1biB0aGlzIApub3RlYm9vayBvciB0aGUgYW5hbHlzZXMgaW4gdGhlIG90aGVyIG5vdGVib29rLCBJJ3ZlIHNldCB0aGlzIHVwIHRvIHNhdmUgdGhlIApgTUFGYCBvYmplY3RzIGFzIGBSRFNgIGZpbGVzLgoKRmlyc3QgbGV0J3MgZXN0YWJsaXNoIHRoZSBmaWxlIHBhdGhzLgoKYGBge3J9CiMgRmlsZSBwYXRocyBmb3IgdGhlIG5lZWRlZCBmaWxlcyBmb3IgdGhpcyBhbmFseXNpcwptZXRhZGF0YV9kaXIgPC0gZmlsZS5wYXRoKHNjcmF0Y2hfZGlyLCAibWV0YWRhdGFfZmlsdGVyZWRfbWFmX3NhbXBsZXMudHN2IikKc3RyZWxrYTJfZGlyIDwtIGZpbGUucGF0aChzY3JhdGNoX2RpciwgInN0cmVsa2EyLlJEUyIpCm11dGVjdDJfZGlyIDwtIGZpbGUucGF0aChzY3JhdGNoX2RpciwgIm11dGVjdDIuUkRTIikKYGBgCgojIyBSZWFkIGluIHRoZSBTdHJlbGthMiBhbmQgTXV0ZWN0MiBkYXRhCgpXZSB3aWxsIHJlYWQgaW4gdGhlIGRhdGEgYXMgYG1hZnRvb2xzYCBvYmplY3RzIGZyb20gYW4gUkRTIGZpbGUsIHVubGVzcyAKYG1hZnRvb2xzYCBoYXMgbm90IGJlZW4gcnVuIG9uIHRoZW0geWV0LgpXZSB3aWxsIHVzZSBgbWV0YWRhdGFgIGFzIHRoZSBgY2xpbmljYWxEYXRhYCBmb3IgdGhlIGBtYWZ0b29sc2Agb2JqZWN0LiAKIApOb3RlOiBJZiB5b3UgdHJ5aW5nIHRvIHJ1biB0aGUgaW5pdGlhbCBzZXQgdXAgc3RlcCBpbiBhIERvY2tlciBjb250YWluZXIsIGl0IAp3aWxsIGxpa2VseSBiZSBvdXQgb2YgbWVtb3J5IGtpbGxlZCwgdW5sZXNzIHlvdSBoYXZlIH41MEdCIHlvdSBjYW4gYWxsb2NhdGUgdG8gCkRvY2tlci4gCgpgYGB7cn0KIyBHZXQgYSB2ZWN0b3Igb2Ygd2hldGhlciB0aGVzZSBleGlzdApmaWxlc19uZWVkZWQgPC0gZmlsZS5leGlzdHMobWV0YWRhdGFfZGlyLCBzdHJlbGthMl9kaXIsIG11dGVjdDJfZGlyKQoKaWYgKGFsbChmaWxlc19uZWVkZWQpKSB7CiAgIyBSZWFkIHRoZSByZWFkeS10by1nbyBmaWxlcyBpZiB0aGVzZSBmaWxlcyBleGlzdAogIG1ldGFkYXRhIDwtIG1ldGFkYXRhIDwtIHJlYWRyOjpyZWFkX3RzdihtZXRhZGF0YV9kaXIpCiAgc3RyZWxrYTIgPC0gcmVhZFJEUyhzdHJlbGthMl9kaXIpCiAgbXV0ZWN0MiA8LSByZWFkUkRTKG11dGVjdDJfZGlyKQp9IGVsc2UgeyAjIElmIGFueSBvZiB0aGUgbmVlZGVkIGZpbGVzIGRvbid0IGV4aXN0LCByZXJ1biB0aGlzIHByb2Nlc3M6CiAgIyBPbmx5IGltcG9ydCB0aGUgc2FtcGxlIG5hbWVzCiAgc3RyZWxrYTJfc2FtcGxlcyA8LSBkYXRhLnRhYmxlOjpmcmVhZChmaWxlLnBhdGgoCiAgICBkYXRhX2RpciwKICAgICJwYnRhLXNudi1zdHJlbGthMi52ZXAubWFmLmd6IgogICksCiAgc2VsZWN0ID0gIlR1bW9yX1NhbXBsZV9CYXJjb2RlIiwKICBza2lwID0gMSwKICBkYXRhLnRhYmxlID0gRkFMU0UKICApICU+JQogICAgZHBseXI6OnB1bGwoIlR1bW9yX1NhbXBsZV9CYXJjb2RlIikKCiAgbXV0ZWN0Ml9zYW1wbGVzIDwtIGRhdGEudGFibGU6OmZyZWFkKGZpbGUucGF0aCgKICAgIGRhdGFfZGlyLAogICAgInBidGEtc252LW11dGVjdDIudmVwLm1hZi5neiIKICApLAogIHNlbGVjdCA9ICJUdW1vcl9TYW1wbGVfQmFyY29kZSIsCiAgc2tpcCA9IDEsCiAgZGF0YS50YWJsZSA9IEZBTFNFCiAgKSAlPiUKICAgIGRwbHlyOjpwdWxsKCJUdW1vcl9TYW1wbGVfQmFyY29kZSIpCgogICMgSXNvbGF0ZSBtZXRhZGF0YSB0byBvbmx5IHRoZSBzYW1wbGVzIHRoYXQgYXJlIGluIHRoZSBkYXRhc2V0cwogIG1ldGFkYXRhIDwtIHJlYWRyOjpyZWFkX3RzdihmaWxlLnBhdGgoZGF0YV9kaXIsICJwYnRhLWhpc3RvbG9naWVzLnRzdiIpKSAlPiUKICAgIGRwbHlyOjpmaWx0ZXIoS2lkc19GaXJzdF9CaW9zcGVjaW1lbl9JRCAlaW4lIGMoc3RyZWxrYTJfc2FtcGxlcywgbXV0ZWN0Ml9zYW1wbGVzKSkgJT4lCiAgICBkcGx5cjo6ZGlzdGluY3QoS2lkc19GaXJzdF9CaW9zcGVjaW1lbl9JRCwgLmtlZXBfYWxsID0gVFJVRSkgJT4lCiAgICBkcGx5cjo6YXJyYW5nZSgpICU+JQogICAgZHBseXI6OnJlbmFtZShUdW1vcl9TYW1wbGVfQmFyY29kZSA9IEtpZHNfRmlyc3RfQmlvc3BlY2ltZW5fSUQpICU+JQogICAgcmVhZHI6OndyaXRlX3RzdihmaWxlLnBhdGgoc2NyYXRjaF9kaXIsICJtZXRhZGF0YV9maWx0ZXJlZF9tYWZfc2FtcGxlcy50c3YiKSkKCiAgIyBSZWFkIGluIG9yaWdpbmFsIHN0cmVsa2EgZmlsZSB3aXRoIG1hZnRvb2xzCiAgc3RyZWxrYTIgPC0gbWFmdG9vbHM6OnJlYWQubWFmKGZpbGUucGF0aChkYXRhX2RpciwgInBidGEtc252LXN0cmVsa2EyLnZlcC5tYWYuZ3oiKSwKICAgIGNsaW5pY2FsRGF0YSA9IG1ldGFkYXRhCiAgKQoKICAjIFNhdmUgdG8gUkRTIHNvIHdlIGRvbid0IGhhdmUgdG8gcnVuIHRoaXMgYWdhaW4KICBzYXZlUkRTKHN0cmVsa2EyLCBzdHJlbGthMl9kaXIpCgogICMgU2FtZSBmb3IgTXVUZWN0MgogIG11dGVjdDIgPC0gbWFmdG9vbHM6OnJlYWQubWFmKGZpbGUucGF0aChkYXRhX2RpciwgInBidGEtc252LW11dGVjdDIudmVwLm1hZi5neiIpLAogICAgY2xpbmljYWxEYXRhID0gbWV0YWRhdGEKICApCiAgc2F2ZVJEUyhtdXRlY3QyLCBtdXRlY3QyX2RpcikKfQpgYGAKCiMjIENvbXBhcmUgbnVtYmVyIG9mIG11dGF0aW9ucyBwZXIgZ2VuZQoKV2Ugd2lsbCBvdXIgcHJlLW1hZGUgZnVuY3Rpb24sIGBjb3JyZWxhdGVfbWFmX3N1bW1hcmllc2AsIHRvIGV2YWx1YXRlIGluaXRpYWwgCmNvcnJlbGF0aW9ucyBiZXR3ZWVuIHRoZSBhbGdvcml0aG1zIG9uIHRoZSBnZW5lLWxldmVsLgpUaGVzZSBjb3JyZWxhdGlvbnMgYWNyb3NzIHZhcmlhbnQgY2xhc3NpZmljYXRpb25zIGFyZSBwbG90dGVkIGFzIGEgYmFycGxvdC4gCgpgYGB7cn0KY29ycmVsYXRlX21hZl9zdW1tYXJpZXMobWFmMSA9IHN0cmVsa2EyLCAKICAgICAgICAgICAgICAgICAgICAgICAgbWFmMiA9IG11dGVjdDIsIAogICAgICAgICAgICAgICAgICAgICAgICBtYWYxX25hbWUgPSAic3RyZWxrYTIiLCAKICAgICAgICAgICAgICAgICAgICAgICAgbWFmMl9uYW1lID0gIm11dGVjdDIiLAogICAgICAgICAgICAgICAgICAgICAgICBzdW1tYXJpemVfYnkgPSAiZ2VuZSIsIAogICAgICAgICAgICAgICAgICAgICAgICBjb3JfbWV0aG9kID0gInBlYXJzb24iKQpgYGAKCiMjIENvbXBhcmUgbnVtYmVyIG9mIG11dGF0aW9ucyBwZXIgc2FtcGxlCgpTaW1pbGFyIHRvIGFib3ZlLCB3ZSB3aWxsIHVzZSBvdXIgcHJlLW1hZGUgZnVuY3Rpb24sIGBjb3JyZWxhdGVfbWFmX3N1bW1hcmllc2AsIAp0byBldmFsdWF0ZSBjb3JyZWxhdGlvbnMgYmV0d2VlbiB0aGUgYWxnb3JpdGhtcyBvbiB0aGUgc2FtcGxlLWxldmVsLgpUaGVzZSBjb3JyZWxhdGlvbnMgYWNyb3NzIHZhcmlhbnQgY2xhc3NpZmljYXRpb25zIGFyZSBwbG90dGVkIGFzIGEgYmFycGxvdC4gCgpgYGB7cn0KY29ycmVsYXRlX21hZl9zdW1tYXJpZXMobWFmMSA9IHN0cmVsa2EyLCAKICAgICAgICAgICAgICAgICAgICAgICAgbWFmMiA9IG11dGVjdDIsIAogICAgICAgICAgICAgICAgICAgICAgICBtYWYxX25hbWUgPSAic3RyZWxrYTIiLCAKICAgICAgICAgICAgICAgICAgICAgICAgbWFmMl9uYW1lID0gIm11dGVjdDIiLAogICAgICAgICAgICAgICAgICAgICAgICBzdW1tYXJpemVfYnkgPSAic2FtcGxlIiwgCiAgICAgICAgICAgICAgICAgICAgICAgIGNvcl9tZXRob2QgPSAicGVhcnNvbiIpCmBgYAoKIyMgUGxvdCBUcmFuc2l0aW9uL1RyYW5zdmVyc2lvbnMKClRoZXNlIGJ1aWx0IGluIG1hZnRvb2xzIGZ1bmN0aW9ucyBwbG90IHRoZSBvdmVyYWxsIHByZXNlbmNlIG9mIHRyYW5zaXRpb25zIGFuZCAKdHJhbnN2ZXJzaW9ucyBhbmQgaXMgcmVjb21tZW5kZWQgYnkgdGhlIFttYWZ0b29scyB2aWduZXR0ZV0oaHR0cHM6Ly9iaW9jb25kdWN0b3Iub3JnL3BhY2thZ2VzL2RldmVsL2Jpb2MvdmlnbmV0dGVzL21hZnRvb2xzL2luc3QvZG9jL21hZnRvb2xzLmh0bWwjNzRfdHJhbnNpdGlvbl9hbmRfdHJhbnN2ZXJzaW9ucykgYXMgYSBtZXRob2Qgb2Ygb2J0YWluaW5nIGFuIG92ZXJhbGwgYmFzZSBjaGFuZ2UKc3VtbWFyeS4gCgojIyMjIFN0cmVsa2EyIHRyYW5zaXRpb24vdHJhbnN2ZXJzaW9uIHBsb3QuCgpgYGB7cn0KbWFmdG9vbHM6OnBsb3RUaVR2KG1hZnRvb2xzOjp0aXR2KHN0cmVsa2EyKSkKYGBgCgojIyMjIE11VGVjdDIgdHJhbnNpdGlvbi90cmFuc3ZlcnNpb24gcGxvdC4KCmBgYHtyfQptYWZ0b29sczo6cGxvdFRpVHYobWFmdG9vbHM6OnRpdHYobXV0ZWN0MikpCmBgYAoKIyMgU2V0IHVwIG5ldyB2YXJpYWJsZXMKCkxldCdzIHNldCB1cCBTdHJlbGthMidzIHZhcmlhYmxlcyBmaXJzdC4gCgpgYGB7cn0KIyBVc2UgdGhlIHByZW1hZGUgZnVuY3Rpb24gdG8gY3JlYXRlIG91ciBuZXcgdmFyaWFibGVzCnN0cmVsa2EyX3ZhZiA8LSBzZXRfdXBfdmFyaWFibGVzKHN0cmVsa2EyKQoKIyBUYWtlIGEgbG9vayBhdCB0aGlzIGRmCnN0cmVsa2EyX3ZhZgpgYGAKCk5vdyB3ZSB3aWxsIGRvIHRoZSBzYW1lIGZvciBNdVRlY3QyLgoKYGBge3J9CiMgVXNlIHRoZSBwcmVtYWRlIGZ1bmN0aW9uIHRvIGNyZWF0ZSBvdXIgbmV3IHZhcmlhYmxlcwptdXRlY3QyX3ZhZiA8LSBzZXRfdXBfdmFyaWFibGVzKG11dGVjdDIpCgojIFRha2UgYSBsb29rIGF0IHRoaXMgZGYKbXV0ZWN0Ml92YWYKYGBgCgojIyBDb21iaW5lIE11VGVjdDIgYW5kIFN0cmVsa2EyIGRhdGEuZnJhbWVzIGludG8gb25lIGRhdGEuZnJhbWUKCkpvaW4gdGhlc2UgZGF0YXNldHMgYmFzZWQgb24gdGhlIGBtdXRhdGlvbl9pZGAgY3JlYXRlZCBieSB0aGUgYHNldF91cF92YXJpYWJsZXNgCmZ1bmN0aW9uLiAKU2F2ZSB0byBhIFRTViBmaWxlIHRvIGJlIHVzZWQgaW4gdGhlIHN1YnNlcXVlbnQgbm90ZWJvb2suCgpgYGB7cn0KIyBNZXJnZSB0aGVzZSBkYXRhLmZyYW1lcyB0b2dldGhlcgp2YWZfZGYgPC0gc3RyZWxrYTJfdmFmICU+JQogIGRwbHlyOjpmdWxsX2pvaW4obXV0ZWN0Ml92YWYsCiAgICBieSA9ICJtdXRhdGlvbl9pZCIsCiAgICBzdWZmaXggPSBjKCIuc3RyZWxrYTIiLCAiLm11dGVjdDIiKQogICkgJT4lCiAgIyBNYWtlIGEgdmFyaWFibGUgdGhhdCBkZW5vdGVzIHdoaWNoIGRhdGFzZXQgaXQgaXMgaW4uCiAgZHBseXI6Om11dGF0ZShkYXRhc2V0ID0gZHBseXI6OmNhc2Vfd2hlbigKICAgIGlzLm5hKEFsbGVsZS5tdXRlY3QyKSB+ICJzdHJlbGthMl9vbmx5IiwKICAgIGlzLm5hKEFsbGVsZS5zdHJlbGthMikgfiAibXV0ZWN0Ml9vbmx5IiwKICAgIFRSVUUgfiAiYm90aCIKICApKSAlPiUKICByZWFkcjo6d3JpdGVfdHN2KGZpbGUucGF0aCgicmVzdWx0cyIsICJjb21iaW5lZF9yZXN1bHRzLnRzdiIpKQpgYGAKCk1ha2UgYSB6aXBwZWQgdXAgdmVyc2lvbiB0aGF0IGNhbiBiZSBzdG9yZWQgb24gR2l0SHViLgoKYGBge3J9CnppcCgKICBmaWxlLnBhdGgoInJlc3VsdHMiLCAiY29tYmluZWRfcmVzdWx0cy50c3YuemlwIiksCiAgZmlsZS5wYXRoKCJyZXN1bHRzIiwgImNvbWJpbmVkX3Jlc3VsdHMudHN2IikKKQpgYGAKClNlc3Npb24gSW5mbzogCgpgYGB7cn0Kc2Vzc2lvbkluZm8oKQpgYGAK